package storage.buffer;

import java.nio.ByteBuffer;
import java.util.Date;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.PriorityQueue;

import storage.file.*;
import utils.Lib;

public class LRUBufferManager extends AbstractBufferManager {
	static {
		identifier = LRUBufferManager.class.getCanonicalName();
	}

	private PriorityQueue<HeapElement> recentlyUsed	= new PriorityQueue<HeapElement>();
//	private LinkedList<BlockID> recentlyUsed = new LinkedList<BlockID>();
	private LinkedList<BlockID> pinPages = new LinkedList<BlockID>();
	
	@Override
	public Block allocatePage(BlockID pageID) {
		if (bufferPool.containsKey(pageID)) {
			if (!pinPages.contains(pageID)) {
				recentlyUsed.remove(new HeapElement(pageID));
				recentlyUsed.add(new HeapElement(pageID));
			}
			return bufferPool.get(pageID);
		} else {
			Block block = FileManagerFactory.getFileManager().read(pageID);
			if (size == BUFFER_SIZE) {
				// replacement
				deallocatePage(recentlyUsed.peek().getBlockID());
			}
			recentlyUsed.add(new HeapElement(pageID));
			bufferPool.put(pageID, block);
			++size;
			return block;
		}
	}
	
	@Override
	public void deallocatePage(BlockID pageID) {
		Lib.assertTrue(bufferPool.containsKey(pageID));
		Lib.assertTrue(!pinPages.contains(pageID));
		
		recentlyUsed.remove(new HeapElement(pageID));
		bufferPool.remove(pageID);
		--size;
	}
	
	@Override
	public void pinPage(BlockID pageID) {
		if (!pinPages.contains(pageID)) {
			Lib.assertTrue(bufferPool.contains(pageID));
			Lib.assertTrue(recentlyUsed.contains(new HeapElement(pageID)));
			
			recentlyUsed.remove(new HeapElement(pageID));
			pinPages.add(pageID);
		}
	}
	
	@Override
	public void unpinPage(BlockID pageID) {
		Lib.assertTrue(pinPages.contains(pageID));
		
		pinPages.remove(pageID);
		recentlyUsed.add(new HeapElement(pageID));
	}
	
	@Override
	public boolean flushAll() {
		// TODO Auto-generated method stub
		return false;
	}
	
	@Override
	public boolean writePage(BlockID pageID, ByteBuffer data) {
		// TODO Auto-generated method stub
		return false;
	}
	
	@Override
	public boolean readPage(BlockID pageID, ByteBuffer data) {
		// TODO Auto-generated method stub
		return false;
	}
	
	private class HeapElement implements Comparable<HeapElement> {
		private BlockID blockID;
		private Date time;
		
		public HeapElement(BlockID blockID) {
			this.blockID = blockID;
			time = new Date();
		}
		
		public BlockID getBlockID() { return blockID; }
		public Date getTime() { return time; }
		
		@Override
		public int compareTo(HeapElement o) {
			return this.time.compareTo(o.time);
		}
		
		@Override
		public int hashCode() {
			return blockID.hashCode();
		}
		
		@Override
		public boolean equals(Object obj) {
			if (obj instanceof HeapElement) {
				return blockID.equals(((HeapElement) obj).blockID);
			} else
				return false;
		}
	}
	
}
